#include "systeminfo.h"

#include <QHostAddress>
#include <QNetworkInterface>
#include <QProcess>
#include <QStorageInfo>

#ifdef Q_OS_WIN
#include <Windows.h>
#include <wlanapi.h>
#include <Psapi.h>
#include <conio.h>
#include <TlHelp32.h>
#include <Psapi.h>
#include <Winternl.h>
#endif

class CPUID {
    uint32_t regs[4];

public:
    explicit CPUID(unsigned int i) {
#ifdef _WIN32
        __cpuid((int *)regs, (int)i);

#else
        asm volatile
                ("cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
            : "a" (i), "c" (0));
        // ECX is set to zero for CPUID function 4
#endif
    }

    const uint32_t &EAX() const {return regs[0];}
    const uint32_t &EBX() const {return regs[1];}
    const uint32_t &ECX() const {return regs[2];}
    const uint32_t &EDX() const {return regs[3];}
};

SystemInfo::SystemInfo()
{

}

QString SystemInfo::getIPAddress()
{
    QString ipAddr;
    for (const QHostAddress &address: QNetworkInterface::allAddresses()) {
        if (address.protocol() == QAbstractSocket::IPv4Protocol && address != QHostAddress(QHostAddress::LocalHost)) {
            if (address.toString().contains("169."))
                continue;
            ipAddr = address.toString();
            break;
        }
    }
    return ipAddr;
}

QList<QPair<QString,QString> > SystemInfo::systemInfo()
{
    QList<QPair<QString, QString>> systemInfo;

#ifdef Q_OS_MACOS
    systemInfo << QPair<QString, QString>(u8"操作系统：", QString(u8"Mac OS X %1").arg(QSysInfo::productVersion()));
#else
    systemInfo << QPair<QString, QString>(u8"operator:", QString(u8"Windows %1").arg(QSysInfo::productVersion()));
#endif

    std::string cpuBrand;

    QList<uint> inputs {0x80000002,0x80000003,0x80000004};

    for (uint input: inputs) {
        CPUID cpuID(input);

        cpuBrand += std::string((const char *)&cpuID.EAX(), 4);
        cpuBrand += std::string((const char *)&cpuID.EBX(), 4);
        cpuBrand += std::string((const char *)&cpuID.ECX(), 4);
        cpuBrand += std::string((const char *)&cpuID.EDX(), 4);
    }

    systemInfo << QPair<QString, QString>(u8"CUP:", QString(cpuBrand.c_str()));
    return systemInfo;
}

QString SystemInfo::getCPUInfo()
{
    return (SystemInfo::systemInfo().end()-1)->second;
    /* 或者通过注册表获取HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0  ProcessorNameString */
}

QString SystemInfo::getOSInfo()
{
    return SystemInfo::systemInfo().begin()->second;
}

QString SystemInfo::getMACAdress() {
    QNetworkInterface thisNet;      //所要使用的网卡
    QList<QNetworkInterface> NetList = QNetworkInterface::allInterfaces();//获取所有网卡信息
    int NetCount = NetList.count();         //统计网卡个数
    for(int Neti = 0;Neti < NetCount; Neti++){   //遍历所有网卡
        thisNet = NetList[Neti];    //将该网卡置为当前网卡
        if(thisNet.isValid() && thisNet.flags().testFlag(QNetworkInterface::IsUp) && thisNet.flags().testFlag(QNetworkInterface::IsRunning)){    //判断该网卡是否是合法
            break;
        }
    }
    return thisNet.hardwareAddress();   //获取该网卡的MAC
}

long long SystemInfo::getTotalRamSize()
{
#ifdef Q_OS_WIN
    MEMORYSTATUSEX memoryStat;
    memoryStat.dwLength = sizeof(MEMORYSTATUSEX);
    GlobalMemoryStatusEx(&memoryStat);
    return memoryStat.ullTotalPhys/1024;
#else

#endif
}

long long SystemInfo::getAvaliableRamSize()
{
#ifdef Q_OS_WIN
    MEMORYSTATUSEX memoryStat;
    memoryStat.dwLength = sizeof(MEMORYSTATUSEX);
    GlobalMemoryStatusEx(&memoryStat);
    return memoryStat.ullAvailPhys/1024;
#else
#endif
}

long long SystemInfo::getRamSize(int pid)
{
#ifdef Q_OS_WIN
    SYSTEM_INFO si;
    GetSystemInfo(&si);

    OSVERSIONINFO osvi;//定义OSVERSIONINFO数据结构对象
    memset(&osvi, 0, sizeof(OSVERSIONINFO));//开空间
    osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);//定义大小
    GetVersionEx (&osvi);//获得版本信息

    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
    if (hSnap) {
        PROCESSENTRY32 entry={sizeof entry};
        /* 获取别的进程好使，自己有时返回空 */
        HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,FALSE,pid);
        if (hProcess) {
            DWORD dwMemProcess = 0;
            if (osvi.dwMajorVersion<6) {
                PROCESS_MEMORY_COUNTERS_EX procCnt;
                if( GetProcessMemoryInfo(hProcess,(PPROCESS_MEMORY_COUNTERS)&procCnt,sizeof(procCnt))) {
                    dwMemProcess = procCnt.WorkingSetSize*1.0f/1024;
                    if(dwMemProcess==0) dwMemProcess = 1;
                    return dwMemProcess;
                }
            } else {
                PSAPI_WORKING_SET_INFORMATION workSet;
                memset(&workSet,0,sizeof(workSet));
                BOOL bOk = QueryWorkingSet(hProcess,&workSet,sizeof(workSet));
                if(bOk || ( !bOk && GetLastError() == ERROR_BAD_LENGTH) ) {
                    int nSize = sizeof(workSet.NumberOfEntries) + workSet.NumberOfEntries*sizeof(workSet.WorkingSetInfo);
                    char* pBuf = new char[nSize];
                    if (pBuf) {
                        QueryWorkingSet(hProcess,pBuf,nSize);
                        PSAPI_WORKING_SET_BLOCK* pFirst = (PSAPI_WORKING_SET_BLOCK*)(pBuf+sizeof(workSet.NumberOfEntries));
                        DWORD dwMem = 0;
                        for (ULONG_PTR nMemEntryCnt = 0;nMemEntryCnt<workSet.NumberOfEntries;nMemEntryCnt++,pFirst++) {
                            if(pFirst->Shared==0) dwMem += si.dwPageSize;
                        }
                        delete pBuf;

                        if(workSet.NumberOfEntries>0) {
                            dwMemProcess = dwMem/1024;
                            return dwMemProcess;
                        }
                    }
                } else {
                    printf("error-QueryWorkingSet:%d\n",GetLastError());
                }
            }

            CloseHandle(hProcess);
        } else {
        }
        CloseHandle(hSnap);
    }
#endif
    return 0;
}


long long SystemInfo::getPeakRamSize(int pid)
{
#ifdef Q_OS_WIN
    HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
    if (handle == nullptr) return 0;

    PROCESS_MEMORY_COUNTERS  process;
    if ( GetProcessMemoryInfo(handle, &process, sizeof(process)) ){
        /* 一般来说：峰值工作集合（pmc.PeakWorkingSetSize）》工作集（pmc.WorkingSetSize）》内存（共享）》内存（专用） */
        /* 工作集（内存） = 内存（专用工作集） + 内存（共享工作集） */
        return process.WorkingSetSize/1024;
    }
    CloseHandle(handle);
#endif
    return 0;
}

long long SystemInfo::getRamSizeOfSelf()
{
#ifdef _WIN32
    return getRamSize(GetCurrentProcessId());
#else
    return 0;
#endif
}

long long SystemInfo::getPeakRamSizeOfSelf()
{
#ifdef _WIN32
    return getPeakRamSize(GetCurrentProcessId());
#else
    return 0;
#endif
}

bool SystemInfo::isWlanConnected()
{
    bool flag = false;
#ifdef Q_OS_WIN
    HANDLE hClient =nullptr;
    DWORD dwCurVersion = 0;
    DWORD dwResult = 0;
    PWLAN_INTERFACE_INFO_LIST pIfList  =nullptr;
    PWLAN_INTERFACE_INFO pIfInfo =nullptr;
    PWLAN_AVAILABLE_NETWORK_LIST pBssList=nullptr;
    dwResult = WlanOpenHandle(2,nullptr,&dwCurVersion,&hClient);
    if(dwResult == ERROR_SUCCESS) {
        dwResult = WlanEnumInterfaces(hClient,nullptr,&pIfList);
        if(dwResult == ERROR_SUCCESS) {
            for (int i = 0; i < static_cast<int>(pIfList->dwNumberOfItems); i++) {
                pIfInfo = reinterpret_cast<WLAN_INTERFACE_INFO *>(&pIfList->InterfaceInfo[i]);
                if(pIfInfo->isState==wlan_interface_state_connected) {
                    flag = true;
                } else if(pIfInfo->isState==wlan_interface_state_disconnected) {
                    flag = false;
                }
            }
        }
    }
    if (pBssList != nullptr) {
        WlanFreeMemory(pBssList);
        pBssList = nullptr;
    }

    if (pIfList != nullptr) {
        WlanFreeMemory(pIfList);
        pIfList = nullptr;
    }
#endif
    return flag;
}

bool SystemInfo::isVirtualSystem()
{
#ifdef Q_OS_WIN
    QProcess process;
    QStringList list;
    list << "/c";
    list << "get-wmiobject win32_computersystem | fl model";
    process.start("powershell",list);
    process.waitForStarted();
    process.waitForFinished();
    QString result = process.readAllStandardOutput();
    QStringList vmlist;
    vmlist << "Parallels" << "VirtualBox" << "VMware";

    for (auto it : vmlist) {
        if(result.contains(it)) {
            return true;
        }
    }

#endif
    return false;
}

void SystemInfo::storageInfo()
{
    foreach (const QStorageInfo &storage, QStorageInfo::mountedVolumes()) {
        if (storage.isValid() && storage.isReady()) {
            if (!storage.isReadOnly()) {
                qDebug() << "rootpath:"<< storage.rootPath();
                qDebug() << "name:" << storage.name()<< "displayname:" << storage.displayName();
                qDebug() << "fileSystemType:" << storage.fileSystemType();
                qDebug() << "size:" << storage.bytesTotal()/1000/1000 << "MB" << "availableSize:" << storage.bytesAvailable()/1000/1000 << "MB";
            }
        }
    }
}

QStringList SystemInfo::getWlanNames()
{
    QStringList list;
#ifdef Q_OS_WIN
    HANDLE hClient =nullptr;
    DWORD dwCurVersion = 0;
    DWORD dwResult = 0;
    PWLAN_INTERFACE_INFO_LIST pIfList  =nullptr;
    PWLAN_INTERFACE_INFO pIfInfo =nullptr;
    PWLAN_AVAILABLE_NETWORK_LIST pBssList=nullptr;
    dwResult = WlanOpenHandle(2,nullptr,&dwCurVersion,&hClient);
    if(dwResult == ERROR_SUCCESS) {
        dwResult = WlanEnumInterfaces(hClient,nullptr,&pIfList);
        if(dwResult == ERROR_SUCCESS) {
            for (int i = 0; i < static_cast<int>(pIfList->dwNumberOfItems); i++) {
                pIfInfo = reinterpret_cast<WLAN_INTERFACE_INFO *>(&pIfList->InterfaceInfo[i]);
                list << QString::fromStdWString(pIfInfo->strInterfaceDescription);
            }
        }
    }
    if (pBssList != nullptr) {
        WlanFreeMemory(pBssList);
        pBssList = nullptr;
    }

    if (pIfList != nullptr) {
        WlanFreeMemory(pIfList);
        pIfList = nullptr;
    }
#endif
    return list;
}
